package malha;

import java.util.Vector;

import controle.CControle;

public class CComposicao {

	private String id;
	private float velMax;
	private int comprimento;
	private float acelMax;
	private float frenMax;
	private Vector<CElementoTrilho> itinerario;
	private int partida;
	private CElementoTrilho trechoChaveAtual;
	private float posicaoAtual;
	private float velAtual;
	private boolean movendoAParaB;
	private float acelAtual;
	private float frenAtual;
	private boolean emMovimento;
	private int tempoConclusao;
	

	/**
	 * Construtor com ID
	 */
	public CComposicao(String id) {
		this.id = id;
	}
	
	
	/**
	 * Acelera uma composicao ate uma determinada velocidade em um determinado tempo
	 * 
	 * @pre modulo da velocidade deve ser maior do que o modulo da velocidade atual --OK
	 * @pre modulo da velocidade deve ser menor ou igual aa velocidade maxima da composicao --OK
	 * @pre tempoConclusao deve ser maior que zero --OK
	 * 
	 * @post A aceleracao atual da composicao eh positiva --OK
	 * @post A aceleracao atual da composicao eh menor ou igual aa aceleracao maxima --OK
	 * @post A frenagem atual da composicao eh zero --OK
	 */
	public void acelerarComposicao(float velocidade, int tempoConclusao) {
		/* Pre */
		assert(Math.abs(velocidade) > Math.abs(this.velAtual));
		assert(Math.abs(velocidade) <= this.velMax);
		assert(tempoConclusao > 0);
		
		this.tempoConclusao = tempoConclusao;
		this.acelAtual = (Math.abs(velocidade) - Math.abs(this.velAtual))/tempoConclusao;
		this.frenAtual = 0;
		
		/* Post */
		assert(this.acelAtual > 0);
		assert(this.acelAtual <= this.acelMax);
		assert(this.frenAtual == 0);
	}
	
	/**
	 * 
	 * @param velocidade
	 * @param aceleracao
	 * 
	 * @pre modulo da velocidade deve ser maior do que o modulo da velocidade atual --OK
	 * @pre modulo da velocidade deve ser menor ou igual aa velocidade maxima da composicao --OK
	 * @pre tempoConclusao deve ser maior que zero --OK
	 * @pre |V|=|Vatual|+a*t
	 * 
	 * @post A aceleracao atual da composicao eh positiva --OK
	 * @post A aceleracao atual da composicao eh menor ou igual aa aceleracao maxima --OK
	 * @post A frenagem atual da composicao eh zero --OK
	 */
	public void acelerarComposicao (float velocidade, int tempoConclusao, float aceleracao){
		/* Pre */
		assert(Math.abs(velocidade) > Math.abs(this.velAtual));
		assert(Math.abs(velocidade) <= this.velMax);
		assert(tempoConclusao > 0);
		assert(CControle.floatEquals(Math.abs(velocidade), Math.abs(this.velAtual)+ aceleracao*tempoConclusao));
		
		this.tempoConclusao = tempoConclusao;
		this.acelAtual = aceleracao;
		this.frenAtual = 0;
		
		/* Post */
		assert(this.acelAtual > 0);
		assert(this.acelAtual <= this.acelMax);
		assert(this.frenAtual == 0);
	}
	
	/**
	 * Freia uma composicao
	 * 
	 * @pre modulo da velocidade deve ser menor do que o modulo velocidade atual --OK
	 * @pre modulo da velocidade deve ser menor ou igual aa velocidade maxima da composicao --OK
	 * @pre tempoConclusao deve ser maior que zero  --OK
	 * 
	 * @post A frenagem atual da composicao eh maior que zero --OK
	 * @post A frenagem atual da composicao eh menor ou igual aa frenagem maxima --OK
	 * @post A aceleracao atual da composicao eh zero --OK
	 */
	public void frearComposicao(float velocidade, int tempoConclusao) {
		/* Pre */
		assert(Math.abs(velocidade) < Math.abs(this.velAtual));
		assert(Math.abs(velocidade) <= this.velMax);
		assert(tempoConclusao > 0);
		
		this.tempoConclusao = tempoConclusao;
		this.frenAtual = Math.abs((Math.abs(velocidade) - Math.abs(this.velAtual))/tempoConclusao);
		this.acelAtual = 0;
		
		/* Post */
		assert(frenAtual > 0);
		assert(frenAtual <= frenMax);
		assert(this.acelAtual == 0);
	}
	
	/**
	 * Freia uma composicao
	 * 
	 * @pre modulo da velocidade deve ser menor do que o modulo velocidade atual --OK
	 * @pre modulo da velocidade deve ser menor ou igual aa velocidade maxima da composicao --OK
	 * @pre tempoConclusao deve ser maior que zero  --OK
	 * 
	 * @post A frenagem atual da composicao eh maior que zero --OK
	 * @post A frenagem atual da composicao eh menor ou igual aa frenagem maxima --OK
	 * @post A aceleracao atual da composicao eh zero --OK
	 */
	public void frearComposicao(float velocidade, int tempoConclusao, float frenagem) {
		/* Pre */
		assert(Math.abs(velocidade) < Math.abs(this.velAtual));
		assert(Math.abs(velocidade) <= this.velMax);
		assert(tempoConclusao > 0);
		
		this.tempoConclusao = tempoConclusao;
		this.frenAtual = frenagem;
		this.acelAtual = 0;
		
		/* Post */
		assert(frenAtual > 0);
		assert(frenAtual <= frenMax);
		assert(this.acelAtual == 0);
	}
	/**
	 * Atualiza a posicao da composicao
	 * 
	 * @post A nova posicao da composicao eh S = s0 + v0*t + (a*t^2)/2  onde
	 * 's0' eh a posicao atual, 'v0' eh a velocidade atual, 'a' eh a aceleracao da composicao e 't' = 1
	 */
	public void atualizarPosicao() {			
		float posAntiga = this.posicaoAtual;
		
		//Sentido da composicao
		if(this.isMovendoAParaB()){
			if(this.acelAtual != 0){ //acelerando ou freando
				this.posicaoAtual = this.posicaoAtual + (this.velAtual) + (this.acelAtual/2);
			}else{
				this.posicaoAtual = this.posicaoAtual + (this.velAtual) - (this.frenAtual/2);
			}
		}else{
			if(this.acelAtual != 0){ //acelerando ou freando
				this.posicaoAtual = this.posicaoAtual - (this.velAtual) - (this.acelAtual/2);
			}else{
				this.posicaoAtual = this.posicaoAtual - (this.velAtual) + (this.frenAtual/2);
			}
		}
		
		/* post */
		float s;
		s = (this.acelAtual > 0) ? this.acelAtual : this.frenAtual;
		s = (this.isMovendoAParaB()) ? s : -s; 
		assert(CControle.floatEqualsDist(posAntiga, this.posicaoAtual));
	}
	
	
	/**
	 * Atualiza a velocidade da composicao
	 * 
	 * @post A nova velocidade da composicao eh V = v0 + a*t   onde 
	 * 'v0' eh a velocidade atual, 'a' eh a aceleracao da composicao e 't' = 1 --OK
	 * 
	 * @post A velocidade atual da composicao eh menor ou igual aa velocidade maxima --OK
	 */
	public void atualizarVelocidade() {	
		float velAntiga = this.velAtual;
		float acelAntiga = (this.acelAtual > 0) ? this.acelAtual : -this.frenAtual;

		if (this.tempoConclusao > 0) {
			if (this.acelAtual > 0) {
				this.velAtual = this.velAtual + this.acelAtual;
				if (this.velAtual > this.velMax) this.velAtual = this.velMax;
			} else if (this.frenAtual > 0) {
				this.velAtual = this.velAtual - this.frenAtual;
				if (this.velAtual < 0) this.velAtual = 0;
			}	
		}
		
		//O trem parou?
		if ((CControle.floatEquals(this.velAtual, 0) && (this.frenAtual > 0))) {
			this.emMovimento = false;
			this.frenAtual = 0;
			this.acelAtual = 0;
			this.velAtual = 0;
			this.tempoConclusao = 0;
		} else {
			this.emMovimento = true;
		}
				
		/* Post */
		assert(CControle.floatEquals(this.velAtual, velAntiga + acelAntiga));
		assert(this.velAtual < this.velMax || CControle.floatEquals(this.velAtual, this.velMax));
		assert(this.velAtual >= 0);
	}
	
	
	/**
	 * Identificador unico da Composicao
	 * 
	 * @return ID da composicao
	 */
	public String getId() {
		return id;
	}
	
	
	/**
	 * Velocidade m�xima da composi��o
	 * 
	 * @return Velocidade m�xima da composi��o
	 */
	public float getVelMax() {
		return velMax;
	}
	
	
	/**
	 * Comprimento da composi��o
	 * 
	 * @return Comprimento da composi��o
	 */
	public int getComprimento() {
		return comprimento;
	}
	
	
	/**
	 * Acelera��o m�xima que a composi��o atinge
	 * 
	 * @return Acelera��o m�xima 
	 */
	public float getAcelMax() {
		return acelMax;
	}
	
	
	/**
	 * Frenagem m�xima que a composi��o atinge
	 * 
	 * @return Frenagem m�xima 
	 */
	public float getFrenMax() {
		return frenMax;
	}
	
	
	/**
	 * Vetor de CElementoTrilho pelos quais a composi��o deve passar
	 * 
	 * @return Itiner�rio da composi��o
	 */
	public Vector<CElementoTrilho> getItinerario() {
		return itinerario;
	}
	
	
	/**
	 * Instante de partida da composicao
	 * 
	 * @return Instante de partida da composicao
	 */
	public int getPartida() {
		return partida;
	}
	
	
	/**
	 * Elemento em que a composicao se encontra 
	 * 
	 * @return Elemento em que a composi��o se encontra
	 */
	public CElementoTrilho getTrechoChaveAtual() {
		return trechoChaveAtual;
	}

	
	/**
	 * Posicao da composicao no trecho em que se encontra
	 * 
	 * @return Posicao da composicao no trecho
	 */
	public float getPosicaoAtual() {
		return posicaoAtual;
	}
	
	
	/**
	 * Velocidade atual da composi��o
	 * 
	 * @return Velocidade atual da composi��o
	 */
	public float getVelAtual() {
		return velAtual;
	}
	
	
	/**
	 * Sentido da composicao.
	 * Da extremidade A para B (true) ou
	 * da extremidade B para A (false)
	 * 
	 * @return De A->B True, de B->A False 
	 */
	public boolean isMovendoAParaB() {
		return movendoAParaB;
	}
	
	
	/**
	 * Acelera��o atual da composi��o
	 * 
	 * @return Acelera��o atual 
	 */
	public float getAcelAtual() {
		return acelAtual;
	}
	
	
	/**
	 * Frenagem atual da composi��o
	 * 
	 * @return Frenagem atual 
	 */
	public float getFrenAtual() {
		return frenAtual;
	}
	
	
	/**
	 * Indica se a composicao estah em movimento (true)
	 * ou estah estacionada (false)
	 * 
	 * @return Retorna se a composicao estag se movendo ou nao
	 */
	public boolean isEmMovimento() {
		return emMovimento;
	}
	
	/**
	 * Tempo restante para a composi��o terminar de acelerar ou frear
	 * 
	 * @return Tempo restante para a composi��o terminar de acelerar ou frear
	 */
	public int getTempoConclusao() {
		return tempoConclusao;
	}
	
	/**
	 * Setta o comprimento da composicao
	 * @param comprimento
	 */
	public void setComprimento(int comprimento) {
		this.comprimento = comprimento;
	}

	/**
	 * Setta o itinerario da composicao
	 * @param itinerario
	 */
	public void setItinerario(Vector<CElementoTrilho> itinerario) {
		this.itinerario = itinerario;
	}

	/**
	 * Setta o instante de partida
	 * @param partida
	 */
	public void setPartida(int partida) {
		this.partida = partida;
	}

	/**
	 * Setta a posicao atual da composicao
	 * @param posicaoAtual
	 */
	public void setPosicaoAtual(float posicaoAtual) {
		this.posicaoAtual = posicaoAtual;
	}
	
	/**
	 * Setta elemento em que a composicao se encontra 
	 * 
	 */
	public void setTrechoChaveAtual(CElementoTrilho elemento) {
		this.trechoChaveAtual = elemento;
	}
	
	/**
	 * Setta aceleracao maxima da composicao
	 * @param acelMax
	 */
	public void setAcelMax(float acelMax) {
		this.acelMax = acelMax;
	}

	/**
	 * Setta frenagem maxima da composicao
	 * @param frenMax
	 */
	public void setFrenMax(float frenMax) {
		this.frenMax = frenMax;
	}
	
	/**
	 * Setta velocidade maxima da composicao
	 * @param velMax
	 */
	public void setVelMax(float velMax) {
		this.velMax = velMax;
	}

	/**
	 * Setta velocidade atual da composicao
	 * @param velAtual
	 */
	public void setVelAtual(float velAtual) {
		this.velAtual = velAtual;
	}

	/**
	 * Setta aceleracao atual
	 * @param acelAtual
	 */
	public void setAcelAtual(float acelAtual) {
		this.acelAtual = acelAtual;
	}

	/**
	 * Setta frenagem atual
	 * @param frenAtual
	 */
	public void setFrenAtual(float frenAtual) {
		this.frenAtual = frenAtual;
	}

	/**
	 * Setta se o trem estah em movimento ou nao
	 * @param emMovimento
	 */
	public void setEmMovimento(boolean emMovimento) {
		this.emMovimento = emMovimento;
	}


	public void setTempoConclusao(int tempoConclusao) {
		this.tempoConclusao = tempoConclusao;
	}


	/**
	 * Setta sentido do movimento
	 * @param movendoAParaB
	 */
	public void setMovendoAParaB(boolean movendoAParaB) {
		this.movendoAParaB = movendoAParaB;
	}

	public boolean equals(Object o){
		boolean ok = false;
		if(o instanceof CComposicao){
			CComposicao temp = (CComposicao)o;
			if(this.id.equals(temp.id)){
				ok = true;
			}
		}
		return ok;
	}
	
	
	@Override
	public String toString() {
		String retorno = "ID: " + this.getId() + " | it: ";
		for (CElementoTrilho elem : this.itinerario) {
			retorno += elem.getId() + ", ";
		}
		return retorno;
	}
	
	
	public void printCComposition(int instante){
		System.out.println("----CComposicao----");
		System.out.println("Instante Atual: "+instante);
		System.out.println("ID: "+this.id);
		System.out.println("Velocidade atual: "+this.velAtual);
		System.out.println("Aceleracao atual: "+this.acelAtual);
		System.out.println("Frenagem atual: "+this.frenAtual);
		System.out.println("Posicao atual: "+this.posicaoAtual);
		System.out.println("Velocidade maxima: "+this.velMax);
		System.out.println("Aceleracao maxima: "+this.acelMax);
		System.out.println("Frenagem maxima: "+this.frenMax);
		System.out.println("Tempo Conclusao: "+this.tempoConclusao);
		System.out.println("");
	}
	
	public static void main(String args[]){
		
		//Cria e configura uma composicao
		CComposicao um = new CComposicao("um");
		um.setComprimento(50);
		um.setPartida(2);
		um.setAcelMax(50);
		um.setFrenMax(50);
		um.setVelMax(200);
		um.setMovendoAParaB(true);
		um.printCComposition(0);
		
		um.acelerarComposicao(30, 3);
		um.printCComposition(0);
		
		//instante 1
		um.atualizarPosicao();
		um.atualizarVelocidade();
		um.printCComposition(1);
		
		//instante 2
		um.atualizarPosicao();
		um.atualizarVelocidade();
		
		//instante 3
		um.atualizarPosicao();
		um.atualizarVelocidade();
		um.printCComposition(3);
		
		um.frearComposicao(20, 1, 10);
		
		//instante 4
		um.atualizarPosicao();
		um.atualizarVelocidade();
		um.printCComposition(4);
		
		//instante 5
		um.atualizarPosicao();
		um.atualizarVelocidade();
		um.printCComposition(5);
		
		System.out.println("Finished..");
	}




}
